package cn.uncode.springcloud.utils.digest;

import cn.uncode.springcloud.utils.exception.ExceptionUtil;
import cn.uncode.springcloud.utils.string.CharsetUtil;

import org.springframework.lang.Nullable;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 加密相关工具类直接使用Spring util封装，减少jar依赖
 *
 */
public final class DigestUtil extends org.springframework.util.DigestUtils {
	
	private DigestUtil() {}


    private static final char[] DIGITS = {
            '0', '1', '2', '3', '4', '5', '6', '7',
            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
    };

    /**
     * 三次盐hash
     *
     * @param pawd
     *         明文密码
     * @param salt
     *         盐值
     * @return
     */
    public static String threeHash(String pawd, String salt) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        if (salt != null) {
            digest.reset();
            digest.update(salt.getBytes("utf-8"));
        }
        byte[] hashed = digest.digest(pawd.getBytes("utf-8"));
        int iterations = 3 - 1; //already hashed once above
        //iterate remaining number:
        for (int i = 0; i < iterations; i++) {
            digest.reset();
            hashed = digest.digest(hashed);
        }
        return new String(byteToChar(hashed));
    }

    /**
     * 字节数组转为字符数组
     *
     * @param data
     * @return
     */
    public static char[] byteToChar(byte[] data) {

        int l = data.length;

        char[] out = new char[l << 1];

        // two characters form the hex value.
        for (int i = 0, j = 0; i < l; i++) {
            out[j++] = DIGITS[(0xF0 & data[i]) >>> 4];
            out[j++] = DIGITS[0x0F & data[i]];
        }

        return out;
    }


    /**
     * Calculates the MD5 digest and returns the value as a 32 character hex string.
     *
	 * @param data Data to digest
	 * @return MD5 digest as a hex string
	 */
	public static String md5Hex(final String data) {
		return DigestUtil.md5DigestAsHex(data.getBytes(CharsetUtil.UTF_8));
	}

	/**
	 * Return a hexadecimal string representation of the MD5 digest of the given bytes.
	 *
	 * @param bytes the bytes to calculate the digest over
	 * @return a hexadecimal digest string
	 */
	public static String md5Hex(final byte[] bytes) {
		return DigestUtil.md5DigestAsHex(bytes);
	}

	private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();

	public static String sha1(String srcStr) {
		return hash("SHA-1", srcStr);
	}

	public static String sha256(String srcStr) {
		return hash("SHA-256", srcStr);
	}

	public static String sha384(String srcStr) {
		return hash("SHA-384", srcStr);
	}

	public static String sha512(String srcStr) {
		return hash("SHA-512", srcStr);
	}

	public static String hash(String algorithm, String srcStr) {
		try {
			MessageDigest md = MessageDigest.getInstance(algorithm);
			byte[] bytes = md.digest(srcStr.getBytes(CharsetUtil.UTF_8));
			return toHex(bytes);
		} catch (NoSuchAlgorithmException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	public static String toHex(byte[] bytes) {
		StringBuilder ret = new StringBuilder(bytes.length * 2);
		for (int i = 0; i < bytes.length; i++) {
			ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
			ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
		}
		return ret.toString();
	}

	public static boolean slowEquals(@Nullable String a, @Nullable String b) {
		if (a == null || b == null) {
			return false;
		}
		return slowEquals(a.getBytes(CharsetUtil.UTF_8), b.getBytes(CharsetUtil.UTF_8));
	}

	public static boolean slowEquals(@Nullable byte[] a, @Nullable byte[] b) {
		if (a == null || b == null) {
			return false;
		}
		if (a.length != b.length) {
			return false;
		}
		int diff = a.length ^ b.length;
		for (int i = 0; i < a.length && i < b.length; i++) {
			diff |= a[i] ^ b[i];
		}
		return diff == 0;
	}

	/**
	 * 自定义加密 先MD5再SHA1
	 *
	 * @param data 数据
	 * @return String
	 */
	public static String encrypt(String data) {
		return sha1(md5Hex(data));
	}

}
